#include "Phnqlet.h"
#include "PhnqletManager.h"
#include "Template.h"
#include "xml_util.h"
#include "util.h"
#include "jsmin.h"
#include "kit.h"
#include <dirent.h>
#include <iostream>
#include <boost/regex.hpp>
#include <iterator>

namespace phnq
{
	namespace phnqlets
	{
		static phnq::kit::log::Logger *log = phnq::kit::log::Logger::get("phnq::phnqlets::Phnqlet");

		static int nextIndex = 1;

		void PhnqletConfig::startTag(string name, string path, map<string,string> attrs)
		{
			if(path == "/phnqlet")
			{
				this->type = attrs["type"];
				this->title = attrs["title"];
				this->alias = attrs["alias"];
				this->extends = attrs["extends"];

				if(this->extends != "")
					this->deps.insert(this->extends);
			}
			else if(path == "/phnqlet/depends")
			{
				this->deps.insert(attrs["type"]);
			}
		}
		
		Phnqlet::Phnqlet(string dir)
		{
			this->dir = dir;
			this->cacheKey = "zero";
			this->tmpltsInited = false;
			this->index = nextIndex++;
			this->i18nStrings = new I18nStringsBundle(dir+"/i18n");
		
			initConfig();

			log->debug("loaded phnqlet: %s (%s)", getType().c_str(), dir.c_str());
		}
	
		Phnqlet::~Phnqlet()
		{
		}

		void Phnqlet::initConfig()
		{
			phnq::kit::xml::SAXHelper saxHelper(&config);
			saxHelper.parse(dir+"/config.xml");
		}
	
		void Phnqlet::initTemplatesIfNeeded()
		{
			boost::mutex::scoped_lock scoped_lock(initTmpltsMutex);
		
			if(this->tmpltsInited)
				return;
		
			DIR *d = opendir(dir.c_str());
			if(d)
			{
				char filename[256];
				char base[64];
				char ext[16];

				struct dirent *pent;
				while(pent = readdir(d))
				{
					util_split_filename(pent->d_name, base, ext);
				
					if(strcmp(ext, "xhtml") == 0)
					{
						sprintf(filename, "%s/%s", dir.c_str(), pent->d_name);
						string type = getType() + "." + base;
						
						Template *t = new Template(filename, type, this);
						templates[base] = t;
					}
				}
				closedir(d);
			}
			this->tmpltsInited = true;
		}
	
		string Phnqlet::getType()
		{
			return config.type;
		}
	
		string Phnqlet::getAlias()
		{
			return config.alias;
		}
	
		Phnqlet* Phnqlet::getSuper()
		{
			if(config.extends == "")
				return NULL;
			else
				return PhnqletManager::getInstance()->get(config.extends);
		}
		
		int Phnqlet::getIndex()
		{
			return index;
		}
	
		string Phnqlet::getDir()
		{
			return dir;
		}

		string Phnqlet::getScriptFile()
		{
			char filename[256];
			sprintf(filename, "%s/script.js", dir.c_str());
			return string(filename);
		}

		string Phnqlet::getScript()
		{
			bool hasScriptFile;
			return getScript(hasScriptFile);
		}
	
		string Phnqlet::getScript(bool &hasScriptFile)
		{
			string s;
			stringbuf sb;
			char chars[80];

			s = "(function(){"; 
			sb.sputn(s.c_str(), s.length());

			s = util_get_file_contents(getScriptFile().c_str());
			if(s.length() == 0)
			{
				sprintf(chars, "phnq.phnqlets.phnqtions[%i]=function(){};", index);
				sb.sputn(chars, strlen(chars));
				hasScriptFile = false;
			}
			else
			{
				util_replace_all(s, "TYPE", getType());
				sb.sputn(s.c_str(), s.length());
				sprintf(chars, "eval(phnq.phnqlets.getScriptFooter(%i));", index);
				sb.sputn(chars, strlen(chars));
				hasScriptFile = true;
			}

			sprintf(chars, "phnq.phnqlets.phnqtionIndexes['%s']=%i;", getType().c_str(), index);
			sb.sputn(chars, strlen(chars));

			s = "})();"; sb.sputn(s.c_str(), s.length());

			return jsmin(sb.str());
		}

		string Phnqlet::getStyleFile()
		{
			char filename[256];
			sprintf(filename, "%s/style.css", dir.c_str());
			return string(filename);
		}
	
		string Phnqlet::getStyle()
		{
			string t = getType();
						
			util_replace_all(t, ".", "\\.");
		
			string styleStr = util_get_file_contents(getStyleFile().c_str());
			util_replace_all(styleStr, "TYPE", t);
		
			boost::regex urlRegex;
			urlRegex.assign("url\\(([^/])(.*?)\\)");
			string r = string("url\\(rel/"+cacheKey+"/"+t+"/$1$2\\)");

		    std::ostringstream os(std::ios::out | std::ios::binary);
		    std::ostream_iterator<char> oi(os);
		    boost::regex_replace(oi, styleStr.begin(), styleStr.end(), urlRegex, r, boost::match_default | boost::format_all);

			styleStr = os.str();
		
			return styleStr;
		}
	
		string Phnqlet::getRelativePath(string path)
		{
			return string("rel/"+cacheKey+"/")+getType()+string("/")+path;
		}
	
		Template* Phnqlet::getTemplate(string name)
		{
			initTemplatesIfNeeded();
		
			return templates[name];
		}
		
		I18nStringsBundle* Phnqlet::getStrings()
		{
			return this->i18nStrings;
		}

		set<Phnqlet*> Phnqlet::getExplicitDependencies()
		{
			set<Phnqlet*> expDeps;
			
			for(set<string>::iterator it=config.deps.begin(); it!=config.deps.end(); it++)
			{
				Phnqlet* p = PhnqletManager::getInstance()->get(*it);
				if(p)
					expDeps.insert(p);
				else
					log->error("%s depends on non-existent %s", getType().c_str(), (*it).c_str());
			}
			
			return expDeps;
		}
	}
}
